package com.pt25.base.util;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;

/**
 * 复杂条件验证工具类
 */
@Slf4j
public class ConditionUtil {
    public static <T> ConditionResult<T> checkCondition(Condition<T> condition, Map<String, Object> compareData) {
        try {
            if (null != condition) {
                List<Condition<T>> conditions = condition.getConditions();
                if (CollectionUtil.isNotEmpty(conditions)) {
                    String rel = condition.getRel().rel;
                    for (Condition<T> conditionData : conditions) {
                        boolean compareResult;
                        if (null != conditionData.getRel() && CollectionUtil.isNotEmpty(conditionData.getConditions())) {
                            compareResult = checkCondition(conditionData, compareData).getResult();
                        } else {
                            compareResult = compareCondition(conditionData, compareData);
                        }
                        if (Rel.AND.rel.equals(rel) && !compareResult) {
                            return createConditionResult(conditionData, false);
                        }
                        if (Rel.OR.rel.equals(rel) && compareResult) {
                            return createConditionResult(conditionData, true);
                        }
                    }
                    if (Rel.AND.rel.equals(rel)) {
                        return createConditionResult(condition, true);
                    }
                    return createConditionResult(condition, false);
                }
                Boolean compareResult = compareCondition(condition, compareData);
                return createConditionResult(condition, compareResult);
            }
        } catch (Exception e) {
            log.error("condition={}", JsonUtil.toJson(condition), e);
        }
        return createConditionResult(condition, false);
    }

    private static <T> ConditionResult<T> createConditionResult(Condition<T> condition, Boolean result) {
        ConditionResult<T> conditionResult = new ConditionResult<>();
        conditionResult.setResult(result);
        conditionResult.setCondition(condition);
        return conditionResult;
    }


    private static <T> Boolean compareCondition(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        if (null != conditionData && null != compareDataMap) {
            String compare = conditionData.getCompare().compare;
            if (null != compareDataMap.get(conditionData.getField())) {
                if (Compare.EQ.compare.equals(compare)) {
                    return eq(conditionData, compareDataMap);
                } else if (Compare.NE.compare.equals(compare)) {
                    return ne(conditionData, compareDataMap);
                } else if (Compare.LT.compare.equals(compare)) {
                    return lt(conditionData, compareDataMap);
                } else if (Compare.LTE.compare.equals(compare)) {
                    return lte(conditionData, compareDataMap);
                } else if (Compare.GT.compare.equals(compare)) {
                    return gt(conditionData, compareDataMap);
                } else if (Compare.GTE.compare.equals(compare)) {
                    return gte(conditionData, compareDataMap);
                } else if (Compare.STW.compare.equals(compare)) {
                    return stw(conditionData, compareDataMap);
                } else if (Compare.EDW.compare.equals(compare)) {
                    return edw(conditionData, compareDataMap);
                } else if (Compare.CN.compare.equals(compare)) {
                    return cn(conditionData, compareDataMap);
                } else if (Compare.IN.compare.equals(compare)) {
                    return in(conditionData, compareDataMap);
                } else if (Compare.BLANK.compare.equals(compare)) {
                    return blank(conditionData, compareDataMap);
                } else if (Compare.NBK.compare.equals(compare)) {
                    return !blank(conditionData, compareDataMap);
                }
            }
        }
        return false;
    }

    private static <T> Boolean blank(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        Object compareData = compareDataMap.get(conditionData.getField());
        if (null != compareData) {
            return StrUtil.isBlank(compareData.toString());
        }
        return false;
    }

    private static <T> Boolean eq(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        Object valueData = conditionData.getValueData();
        Object compareData = compareDataMap.get(conditionData.getField());
        if (null != valueData && null != compareData) {
            return valueData.equals(compareData);
        }
        return false;
    }

    private static <T> Boolean ne(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        return !eq(conditionData, compareDataMap);
    }

    private static <T> Boolean lt(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        Object valueData = conditionData.getValueData();
        Object compareData = compareDataMap.get(conditionData.getField());
        if (null != valueData && null != compareData)
            return Double.parseDouble(compareData + "") < Double.parseDouble(valueData + "");
        return false;
    }

    private static <T> Boolean gt(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        Object valueData = conditionData.getValueData();
        Object compareData = compareDataMap.get(conditionData.getField());
        if (null != valueData && null != compareData)
            return Double.parseDouble(compareData + "") > Double.parseDouble(valueData + "");
        return false;
    }

    private static <T> Boolean lte(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        return !gt(conditionData, compareDataMap);
    }

    private static <T> Boolean gte(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        return !lt(conditionData, compareDataMap);
    }

    private static <T> Boolean stw(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        Object valueData = conditionData.getValueData();
        Object compareData = compareDataMap.get(conditionData.getField());
        if (null != valueData && null != compareData)
            return compareData.toString().startsWith(valueData.toString());
        return false;
    }

    private static <T> Boolean edw(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        Object valueData = conditionData.getValueData();
        Object compareData = compareDataMap.get(conditionData.getField());
        if (null != valueData && null != compareData)
            return compareData.toString().endsWith(valueData.toString());
        return false;
    }

    private static <T> Boolean cn(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        Object valueData = conditionData.getValueData();
        Object compareData = compareDataMap.get(conditionData.getField());
        if (valueData == null || compareData == null) {
            return false;
        }
        return compareData.toString().contains(valueData.toString());
    }

    private static <T> Boolean in(Condition<T> conditionData, Map<String, Object> compareDataMap) {
        Object valueData = conditionData.getValueData();
        Object compareData = compareDataMap.get(conditionData.getField());
        if (null != valueData && null != compareData) {
            List<String> valueDataList = JsonUtil.toBeanList(valueData, String.class);
            return valueDataList.contains(compareData + "");
        }
        return false;
    }


    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class ConditionResult<T> {
        private Condition<T> condition;
        private Boolean result;
    }

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Condition<T> {
        private Rel rel;
        private String field;
        private Compare compare;
        private ValueType valueType;
        private Object valueData;
        /**
         * 条件满足时对应的操作
         */
        private T operation;

        private List<Condition<T>> conditions;

    }

    public static enum Rel {
        AND("and"), OR("or");
        private final String rel;

        Rel(String rel) {
            this.rel = rel;
        }
    }

    public static enum ValueType {
        NUM("num", "数字类型"),
        STR("str", "字符串类型"),
        LIST("list", "集合类型");

        ValueType(String name, String description) {
        }

    }


    public static enum Compare {
        EQ("=", "等于"),
        NE("!=", "不等于"),
        LT("<", "小于"),
        LTE("<=", "小于等于"),
        GT(">", "大于"),
        GTE(">=", "大于等于"),
        STW("stw", "以什么开头"),
        EDW("edw", "以什么结尾"),
        CN("cn", "字符串包含"),
        IN("in", "列表包含"),
        BLANK("blank", "为空"),
        NBK("nbk", "不为空");

        private final String compare;

        Compare(String compare, String description) {
            this.compare = compare;
        }
    }
}
